package com.example.demo.utils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.BiFunction;

/**
 * @author FENGYU
 * @description
 * @date 2023/8/15 15:50
 */
public class DoubleUtil {

    public static final Double DOUBLE_ZERO = 0.0d;

    enum MathEnum{
        ADD, SUB, MUL,DIV
    }

    /** 最大除法精度 */
    private static final Integer MAX_DIV_SCALE = 4;
    private static final Integer DEF_DIV_SCALE = 2;
    private static final int DEFAULT_SCALE = 2;

    private static Map<MathEnum, BiFunction<BigDecimal, BigDecimal, BigDecimal>> actionMap = new HashMap<>();
    static {
        actionMap.put(MathEnum.ADD, (v1, v2) -> v1.add(v2));
        actionMap.put(MathEnum.SUB, (v1, v2) -> v1.subtract(v2));
        actionMap.put(MathEnum.MUL, (v1, v2) -> v1.multiply(v2));
        actionMap.put(MathEnum.DIV, (v1, v2) -> v1.divide(v2,MAX_DIV_SCALE,  BigDecimal.ROUND_HALF_UP));
    }

    private static Double math(Double v1, Double v2, MathEnum type, int scale){
        BigDecimal b1 = BigDecimal.valueOf(nullToZero(v1));
        BigDecimal b2 = BigDecimal.valueOf(nullToZero(v2));
        BigDecimal apply = actionMap.get(type).apply(b1, b2);
        if(scale > 0){
            apply = apply.setScale(scale, BigDecimal.ROUND_HALF_UP);
        }
        return new Double(apply.doubleValue());
    }

    /**
     * * 两个Double数相加 *
     *
     * @param v1 *
     * @param v2 *
     * @return Double
     */
    public static Double add(Double v1, Double v2) {
        return math(v1, v2, MathEnum.ADD, DEFAULT_SCALE);
    }

    public static Double add(Double v1, Double ... v2) {
        if(ValidationUtil.isEmpty(v2) || v2.length == 0){
            return v1;
        }

        for (int i = 0; i < v2.length; i++) {
            v1 = math(v1, nullToZero(v2[i]), MathEnum.ADD, DEFAULT_SCALE);
        }
        return v1;
    }
    /**
     * * 两个Double数相减 *
     *
     * @param v1 *
     * @param v2 *
     * @return Double
     */
    public static Double sub(Double v1, Double v2) {
        return math(v1, v2, MathEnum.SUB, DEFAULT_SCALE);
    }

    /**
     * * 两个Double数相减 *
     *
     * @param v1 *
     * @param v2 *
     * @return Double
     */
    public static Double sub(Double v1, Double ... v2) {
        if(ValidationUtil.isEmpty(v2) || v2.length == 0){
            return v1;
        }

        for (int i = 0; i < v2.length; i++) {
            v1 = math(v1, nullToZero(v2[i]), MathEnum.SUB, DEFAULT_SCALE);
        }
        return v1;
    }

    /**
     * * 两个Double数相减 *
     * 保留指定小数
     * @param v1 *
     * @param v2 *
     * @return Double
     */
    public static Double sub(Double v1, Double v2, Integer num) {
        return math(v1, v2, MathEnum.SUB, num);
    }

    /**
     * * 两个Double数相乘 *
     *
     * @param v1 *
     * @param v2 *
     * @return Double
     */
    public static Double mul(Double v1, Double v2) {
        return mul(v1, v2, DEFAULT_SCALE);
    }

    public static Double mul(Double v1, Double v2, Integer scale) {
        return math(v1, v2, MathEnum.MUL, scale);
    }

    public static Double div(Double dividend, Double divisor) {
        return div(dividend, divisor, DEF_DIV_SCALE);
    }

    /**
     * 提供（相对）精确的除法运算。 当发生除不尽的情况时，由scale参数指定精度，以后的数字四舍五入。
     *
     * @param dividend
     *            被除数
     * @param divisor
     *            除数
     * @param scale
     *            表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */
    public static Double div(Double dividend, Double divisor, Integer scale) {
        if (scale < 0) {
            throw new IllegalArgumentException(
                    "The scale must be a positive integer or zero");
        }

        if(scale > 4){
            throw new IllegalArgumentException(
                    "The scale must be less then 4 ");
        }

        if(compare(nullToZero(divisor), 0.0d) == 0){
            throw new IllegalArgumentException(
                    "The divisor must be not equals zero or null ");
        }

        return math(dividend, divisor, MathEnum.DIV, scale);
    }

    /**
     * double类型数值比较
     *
     * @author zhubb
     * @createTim1e 2018年12月20日 下午4:22:11
     * @param d1
     * @param d2
     * @return
     */
    public static int compare(Double d1, Double d2){
        return Double.compare(nullToZero(d1), nullToZero(d2));
    }

    /***
     * 如果传入为空，则返回 0.0d
     * @Author zhubingbing
     * @Date 2021/5/13 16:35
     * @param param
     * @return java.lang.Double
     */
    public static Double nullToZero(Double param){
        return Optional.ofNullable(param).orElse(0.0d);
    }

    /**
     *
     * 四舍五入保留小数
     * @Author zhubingbing
     * @Date 2022/12/6 12:27
     * @param param 输入参数
     * @param scale 保留小数位数
     * @return java.lang.Double
     */
    public static Double roundScale(Double param,  Integer scale){

        if (ValidationUtil.isEmpty(param)) {
            return 0.0d;
        }

        if(scale < 0){
            throw new IllegalArgumentException("the scale must be greater than zero ");
        }

        return new BigDecimal(param).setScale(scale, BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     *
     *
     * @Author zhubingbing
     * @Date 2022/12/6 12:32
     * @param param
     * @param scale
     * @return java.lang.Double
     */
    public static Double roundScaleAndGreaterZero(Double param,  Integer scale){
        return Math.max(roundScale(param, scale), 0);
    }

    /**
     * 保留2位小数 采用四舍五入 <br>
     * ROUND_HALF_UP: 遇到.5的情况时往上近似,例: 1.5 -> 2 <br>
     * ROUND_HALF_DOWN : 遇到.5的情况时往下近似,例: 1.5 -> 1 <br>
     *
     * @author zmx
     * @createTim1e 2022/8/3 11:57:40
     * @param param 参数
     * @param isLessThanZero 是否<0返回0 true:小于0则返回0 false:为负数则返回负数
     * @return
     */
    public static double keepTwo(Double param,boolean isLessThanZero){
        double doubleValue;

        if (isLessThanZero){
            doubleValue = new BigDecimal(param).setScale(DEF_DIV_SCALE, BigDecimal.ROUND_HALF_UP).doubleValue();
            return doubleValue < 0 ? 0 : doubleValue;
        }
        return new BigDecimal(param).setScale(DEF_DIV_SCALE, BigDecimal.ROUND_HALF_UP).doubleValue();
    }


    /**
     * 学生科目成绩实体
     */
    public static class TestScore {
        private String studentName;

        private String subject;

        private String semester;

        private Double score;

        private  String classGrade;

        public TestScore(String studentName, String subject, String semester, Double score, String classGrade) {
            this.studentName = studentName;
            this.subject = subject;
            this.semester = semester;
            this.score = score;
            this.classGrade = classGrade;
        }

        // 省略get set toString ...

        public String getStudentName() {
            return studentName;
        }

        public void setStudentName(String studentName) {
            this.studentName = studentName;
        }

        public String getSubject() {
            return subject;
        }

        public void setSubject(String subject) {
            this.subject = subject;
        }

        public String getSemester() {
            return semester;
        }

        public void setSemester(String semester) {
            this.semester = semester;
        }

        public Double getScore() {
            return score;
        }

        public void setScore(Double score) {
            this.score = score;
        }

        public String getClassGrade() {
            return classGrade;
        }

        public void setClassGrade(String classGrade) {
            this.classGrade = classGrade;
        }
    }

}
